﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;

namespace TestWcfRestService
{
    public static class HttpHelper
    {
        public static IDictionary<string, string> ToDictionary(this NameValueCollection collection)
        {
            var dict = new Dictionary<string, string>();
            foreach (var key in collection.AllKeys)
                dict.Add(key, collection[key]);

            return dict;
        }

        #region -= Request to Json =-

        private static string ReadStreamText(Stream stream, Encoding encoding)
        {
            try
            {
                if (!stream.CanRead)
                    return "[Error: cannot read stream]";

                if (stream.CanSeek)
                    stream.Seek(0, SeekOrigin.Begin);

                var sr = new StreamReader(stream, encoding);
                var text = sr.ReadToEnd();

                if (stream.CanSeek)
                    stream.Seek(0, SeekOrigin.Begin);

                return text;
            }
            catch (Exception ex)
            {
                return "[Cannot read stream: " + ex.Message + "]";
            }
        }

        public static string GetRequestData(HttpRequest request)
        {
            return ReadStreamText(request.InputStream, request.ContentEncoding);
        }

        public static object GetRequestJsonObject(HttpRequest request, string requestTime)
        {
            object requestBody = GetRequestData(request);
            try
            {
                requestBody = JsonHelper.FromJson<object>(requestBody as string);
            }
            catch
            { }

            string userName = null;
            try
            {
                userName = request.LogonUserIdentity.Name;
            }
            catch
            { }

            return new
            {
                RequestTime = requestTime,
                RequestUrl = request.Url.OriginalString,
                RequestType = request.RequestType,
                RequestBody = requestBody,
                //AcceptTypes = request.AcceptTypes,
                //AnonymousID = request.AnonymousID,
                //ApplicationPath = request.ApplicationPath,
                //AppRelativeCurrentExecutionFilePath = request.AppRelativeCurrentExecutionFilePath,
                ContentEncoding = null == request.ContentEncoding ? null : request.ContentEncoding.EncodingName,
                ContentLength = request.ContentLength,
                ContentType = request.ContentType,
                //Cookies = request.Cookies.Cast<HttpCookie>()
                //    .Select(it => new
                //    {
                //        Domain = it.Domain,
                //        Expires = it.Expires,
                //        HasKeys = it.HasKeys,
                //        HttpOnly = it.HttpOnly,
                //        Name = it.Name,
                //        Path = it.Path,
                //        Secure = it.Secure,
                //        Value = it.Value,
                //        Values = it.Values.ToDictionary()
                //    }).ToArray(),
                //Files = request.Files.Cast<HttpPostedFile>()
                //    .Select(it => new
                //    {
                //        ContentLength = it.ContentLength,
                //        ContentType = it.ContentType,
                //        FileName = it.FileName
                //    }).ToArray(),

                Form = request.Form.ToDictionary(),
                Headers = request.Headers.ToDictionary(),
                LogonUserIdentity = userName
                //QueryString = request.QueryString.ToDictionary(),
                //RawUrl = request.RawUrl,
                //ServerVariables = request.ServerVariables.ToDictionary(),
                //UrlReferrer = null == request.UrlReferrer ? null : request.UrlReferrer.OriginalString,
                //UserLanguages = request.UserLanguages
            };
        }

        public static string GetRequestJson(HttpRequest request, string requestTime)
        {
            var req = GetRequestJsonObject(request, requestTime);
            return req.ToJson();
        }

        public static Dictionary<string, object> GetRequestDictionary(HttpRequest request, string requestTime)
        {
            var json = GetRequestJson(request, requestTime);
            return json.FromJson<Dictionary<string, object>>();
        }

        #endregion

        #region -= Response to Json =-

        public static string GetResponseData(HttpResponse response)
        {
            try
            {
                var output = response.Output;
                var outputType = output.GetType();
                var encoding = response.ContentEncoding;

                var bind = BindingFlags.Public | BindingFlags.NonPublic
                    | BindingFlags.Instance | BindingFlags.IgnoreCase
                    | BindingFlags.GetField;

                //HttpWriter._charBuffer.Length = 1024, 
                //a new IHttpResponseElement will be created if more data need to be stored, and added to HttpWriter._buffers
                var buffers = outputType
                    .GetField("_buffers", bind)
                    .GetValue(output) as ArrayList;

                var sb = new StringBuilder(buffers.Count * 512);
                if (null != buffers && buffers.Count > 0)
                {
                    Assembly sysWeb = null;
                    try
                    {
                        sysWeb = Assembly.Load("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
                    }
                    catch
                    {
                        sysWeb = Assembly.Load("System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
                    }

                    var httpReponseElementType = null == sysWeb ? null : sysWeb.GetType("System.Web.IHttpResponseElement");
                    var fnGetBytes = null == httpReponseElementType ? null : httpReponseElementType.GetMethod("GetBytes");

                    for (int i = 0; i < buffers.Count; i++)
                    {
                        var buffer = buffers[i];
                        var data = fnGetBytes.Invoke(buffer, null) as byte[];
                        sb.Append(encoding.GetString(data));
                    }
                }

                var charBuffer = (char[])outputType.GetField("_charBuffer", bind).GetValue(output);
                var charBufferLength = (int)outputType.GetField("_charBufferLength", bind).GetValue(output);
                var charBufferFree = (int)outputType.GetField("_charBufferFree", bind).GetValue(output);
                for (int i = 0; i < charBufferLength - charBufferFree; i++)
                {
                    sb.Append(charBuffer[i]);
                }

                return sb.ToString();
            }
            catch (Exception ex)
            {
                return ex.Message + Environment.NewLine + ex.StackTrace;
            }
        }

        private static string GetResponseDataByFilter(HttpResponse response)
        {
            var filter = response.Filter;
            if (filter is ResponseFilterStream)
                return (filter as ResponseFilterStream).GetResponseOutput();

            return GetResponseData(response);
        }

        public static void RegisterToCaptureResponse(HttpResponse response)
        {
            var filter = new ResponseFilterStream(response.Filter, response.ContentEncoding);
            response.Filter = filter;
        }

        public static object GetResponseJsonObject(HttpResponse response, string endRequestTime, Func<string, object> fnResponseBodyToObject = null)
        {
            var responseBody = GetResponseDataByFilter(response);
            object bodyObj = null;

            #region -= deserialize response body =-
            if (null != fnResponseBodyToObject)
            {
                try
                {
                    bodyObj = fnResponseBodyToObject(responseBody);
                }
                catch { }
            }

            //if the response body is a json string, deserialize it to an object
            if (null == bodyObj)
            {
                try
                {
                    bodyObj = JsonHelper.FromJson<object>(responseBody);
                }
                catch { }
            }
            #endregion

            return new
            {
                EndRequestTime = endRequestTime,
                //Buffer = response.Buffer,
                //BufferOutput = response.BufferOutput,
                //CacheControl = response.CacheControl,
                //CacheMinutes = response.Expires,
                Charset = response.Charset,
                ContentEncoding = null == response.ContentEncoding ? null : response.ContentEncoding.EncodingName,
                ContentType = response.ContentType,
                //Cookies = response.Cookies.OfType<HttpCookie>()
                //    .Select(it => new
                //    {
                //        Domain = it.Domain,
                //        Expires = it.Expires,
                //        HasKeys = it.HasKeys,
                //        HttpOnly = it.HttpOnly,
                //        Name = it.Name,
                //        Path = it.Path,
                //        Secure = it.Secure,
                //        Value = it.Value,
                //        Values = it.Values.ToDictionary()
                //    }).ToArray(),

                //Expires = response.ExpiresAbsolute,
                HeaderEncoding = null == response.HeaderEncoding ? null : response.HeaderEncoding.EncodingName,
                //IsClientConnected = response.IsClientConnected,
                IsRequestBeingRedirected = response.IsRequestBeingRedirected,
                RedirectLocation = response.RedirectLocation,
                Status = response.Status,
                ResponseBody = bodyObj ?? responseBody,
                //StatusCode = response.StatusCode,
                //StatusDescription = response.StatusDescription,
                //SuppressContent = response.SuppressContent,
                //TrySkipIisCustomErrors = response.TrySkipIisCustomErrors
            };
        }

        public static string GetResponseJson(HttpResponse response, string endRequestTime, Func<string, object> fnResponseBodyToObject = null)
        {
            var req = GetResponseJsonObject(response, endRequestTime, fnResponseBodyToObject);
            return req.ToJson();
        }

        public static Dictionary<string, object> GetResponseDictionary(HttpResponse response, string endRequestTime, Func<string, object> fnResponseBodyToObject = null)
        {
            var json = GetResponseJson(response, endRequestTime, fnResponseBodyToObject);
            return json.FromJson<Dictionary<string, object>>();
        }

        #endregion
    }
}